package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.heima.article.dto.ArticleDto;
import com.heima.article.dto.ArticleHomeDto;
import com.heima.article.dto.ArticleInfoDto;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApArticleContent;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.IApArticleContentService;
import com.heima.article.service.IApArticleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.task.ArticleHtmlTasks;
import com.heima.common.dto.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author gyq
 * @since 2022-06-29
 */
@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements IApArticleService {

    @Autowired
    private IApArticleContentService apArticleContentService;
    @Autowired
    private ArticleHtmlTasks articleHtmlTasks;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public ResponseResult<Long> saveApArticle(ArticleDto dto) {
        // 先判断是否有id
        if (dto.getId() == null){ // 新增
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto,apArticle);
            apArticle.setCreatedTime(new Date());
            // 新增文章信息
            this.save(apArticle);
            // 新增文章内容
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentService.save(apArticleContent);

            // 生成静态页面的方法
            articleHtmlTasks.createArticleHtml(apArticle,dto.getContent());

            return ResponseResult.okResult(apArticle.getId());
        }else {
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto,apArticle);
            apArticle.setCreatedTime(new Date());
            // 修改文章信息
            this.updateById(apArticle);
            // 修改文章内容
            // update ApArticleContent set content=?? where ArticleId=??
            LambdaUpdateWrapper<ApArticleContent> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(ApArticleContent::getContent,dto.getContent());
            updateWrapper.eq(ApArticleContent::getArticleId,dto.getId());
            apArticleContentService.update(updateWrapper);

            // 生成静态页面的方法
            articleHtmlTasks.createArticleHtml(apArticle,dto.getContent());

            return ResponseResult.okResult(apArticle.getId());
        }
    }

    //加载app端文章列表
    @Override
    public ResponseResult load(ArticleHomeDto dto, int type) { //0代表初次加载和加载更新  1代表加载更新
        // 判断是否是首页加载数据，如果是首页的话，先从redis中查询数据
        if (dto.getLoaddir() == 1){
            // 先从redis中查询数据，按照分值从大到小排序
            String key = "hot_article_first_page_"+ dto.getChannelId();
            long start = 0;
            long end = dto.getSize() -1;
            Set<String> strings = redisTemplate.boundZSetOps(key).reverseRange(start, end); // reverseRange根据分值降序查询
            List<ApArticle> articles = new ArrayList<>();
            for (String string : strings) {
                ApArticle article = JSON.parseObject(string, ApArticle.class);
                articles.add(article);
            }
            // 判断查询到的数据是否>= dto.getSize()  意味着Redis中的数量不够的话 就从mysql中查询了
            if (articles.size() >= dto.getSize()){
                return ResponseResult.okResult(articles);
            }
        }




        // 条件查询
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        // 查询条件：频道id  如果是0 就是查询所有频道
        if (dto.getChannelId()!= 0) {
            queryWrapper.eq(ApArticle::getChannelId,dto.getChannelId());
        }
        // 判断加载最新文章,查询比传递的最大时间更大的文章
        if (type == 0){
            // 0代表初次加载和更新  发布时间>maxTime
            queryWrapper.gt(ApArticle::getPublishTime,dto.getMaxTime());
        }else {
            // 1代表加载更多  发布时间<minTime
            queryWrapper.lt(ApArticle::getPublishTime,dto.getMinTime());
        }
        // 隐藏了两个条件：是否下架  是否删除
        queryWrapper.eq(ApArticle::getIsDelete,false);
        queryWrapper.eq(ApArticle::getIsDown,false);
        // 按照发布时间倒序排列
        queryWrapper.orderByDesc(ApArticle::getPublishTime);
        //  分页查询,默认显示10条,前端会传递
        Page<ApArticle> page = new Page<>(1,dto.getSize());
        Page<ApArticle> page1 = this.page(page, queryWrapper);


        return ResponseResult.okResult(page1.getRecords());
    }

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        // 查询两个表
        // 根据文章id查询ap_article表
        ApArticle apArticle = this.getById(dto.getArticleId());
        if (apArticle== null) {
            log.error("查不到数据，文章id是:"+dto.getArticleId());
            ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 根据文章id查询ap_article_content表
        // select * from ap_article_content where article_id=?
        LambdaQueryWrapper<ApArticleContent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApArticleContent::getArticleId,dto.getArticleId());
        ApArticleContent apArticleContent = apArticleContentService.getOne(queryWrapper);

        Map resultMap = new HashMap();
        resultMap.put("config",apArticle);
        resultMap.put("content",apArticleContent);

        return ResponseResult.okResult(resultMap);



    }

    @Override  // 定时计算热点文章，并放入到Redis中
    public void computeHotArticle() {
        // 查询前300天  未下架未删除的文章

        // 获取300天之前的日期
        // 使用了joda-time中的工具类 ，因为最近测试数据少，这里可以查询200天之前的数据
        Date beforeDate = DateTime.now().minusDays(300).toDate();
        // 获取今天时间和 5天前的时间
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApArticle::getIsDown,false);
        queryWrapper.eq(ApArticle::getIsDelete,false);
        queryWrapper.gt(ApArticle::getPublishTime,beforeDate);

        List<ApArticle> list = this.list(queryWrapper);

        // 计算文章的分值：
        for (ApArticle apArticle : list) {
            Integer score = computeScore(apArticle);
            // 放入到Redis中 结构： 使用zset  zset中的key:hot_article_first_page_频道ID

            ApArticle cache = new ApArticle();
            cache.setId(apArticle.getId());
            cache.setTitle(apArticle.getTitle());
            cache.setAuthorId(apArticle.getAuthorId());
            cache.setAuthorName(apArticle.getAuthorName());
            cache.setLayout(apArticle.getLayout());
            cache.setImages(apArticle.getImages());
            cache.setStaticUrl(apArticle.getStaticUrl());
            cache.setPublishTime(apArticle.getPublishTime());


            // 再放到各自的频道上推荐的文章
            redisTemplate.boundZSetOps("hot_article_first_page_"+apArticle.getChannelId()).add(JSON.toJSONString(cache),score);//TODO  7.8修改这的值
            // 0代表是首页上推荐的文章
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(apArticle),score);
        }

    }

    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        // 阅读 +1  点赞 +3  评论 +5  收藏 +8
        if (apArticle.getViews() != null){
            score += apArticle.getViews()*1;
        }
        if (apArticle.getLikes() != null){
            score += apArticle.getLikes()*3;
        }
        if (apArticle.getComment() != null){
            score += apArticle.getComment()*5;
        }
        if (apArticle.getCollection() != null){
            score += apArticle.getCollection()*8;
        }
        return score;
    }


    @Override
    public void updateHotArticle(ArticleStreamMessage articleStreamMessage) {
        Long articleId = articleStreamMessage.getArticleId();
        ApArticle apArticle = this.getById(articleId);

        ApArticle cache = new ApArticle();
        cache.setId(apArticle.getId());
        cache.setTitle(apArticle.getTitle());
        cache.setAuthorId(apArticle.getAuthorId());
        cache.setAuthorName(apArticle.getAuthorName());
        cache.setLayout(apArticle.getLayout());
        cache.setImages(apArticle.getImages());
        cache.setStaticUrl(apArticle.getStaticUrl());
        cache.setPublishTime(apArticle.getPublishTime());

        // 修改Redis中缓存的那个文章的分值
        Double score = redisTemplate.boundZSetOps("hot_article_first_page_" + apArticle.getChannelId()).score(JSON.toJSONString(cache));

        Integer todayScore = getTodayScore(articleStreamMessage);
        if (score != null){  // 表示Redis中有这个数据， 把分值修改再放回到Redis中
            redisTemplate.boundZSetOps("hot_article_first_page_"+apArticle.getChannelId()).add(JSON.toJSONString(cache),score+todayScore);
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(cache),score+todayScore);
        }else {  // 表示Redis中有这个数据， 直接放到Redis中
            redisTemplate.boundZSetOps("hot_article_first_page_"+apArticle.getChannelId()).add(JSON.toJSONString(cache),todayScore);
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(cache),todayScore);
        }

        // 修改文章表中这个文章的各个操作
        // 3、更新mysql中ApArticle中的阅读数、点赞数、评论数、收藏数
        LambdaUpdateWrapper<ApArticle> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApArticle::getId,articleId);

        updateWrapper.setSql("views=views+"+articleStreamMessage.getView());
        updateWrapper.setSql("likes=likes+"+articleStreamMessage.getLike());
        updateWrapper.setSql("comment=comment+"+articleStreamMessage.getComment());
        updateWrapper.setSql("collection=collection+"+articleStreamMessage.getCollect());
        this.update(updateWrapper);

    }

    // 计算当日的增量分数  当日的操作整体权重*3
    private Integer getTodayScore(ArticleStreamMessage articleStreamMessage) {
        // 阅读数*1+点赞数*3+评论数*5+收藏数*8

        /*int score = 0;
        score += articleStreamMessage.getView() * 1 * 3;
        score += articleStreamMessage.getLike() * 3 * 3;
        score += articleStreamMessage.getComment() * 5 * 3;
        score += articleStreamMessage.getCollect() * 8 * 3;
        return score;*/

        Long score = (articleStreamMessage.getView()*1+articleStreamMessage.getLike()*3+articleStreamMessage.getComment()*5+articleStreamMessage.getCollect()*8)*3;
        return score.intValue();
    }
}
